﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OsgViewer;

namespace osgViewer
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // use an ArgumentParser object to manage the program arguments.
            var arguments = new Osg.ArgumentParser(args);

            arguments.ApplicationUsage.ApplicationName = arguments.ApplicationName;
            arguments.ApplicationUsage.Description = arguments.ApplicationName + " is the standard OpenSceneGraph example which loads and visualises 3d models.";
            arguments.ApplicationUsage.CommandLineUsage = arguments.ApplicationName + " [options] filename ...";
            arguments.ApplicationUsage.addCommandLineOption("--image <filename>", "Load an image and render it on a quad");
            arguments.ApplicationUsage.addCommandLineOption("--dem <filename>", "Load an image/DEM and render it on a HeightField");
            arguments.ApplicationUsage.addCommandLineOption("--login <url> <username> <password>", "Provide authentication information for http file access.");
            arguments.ApplicationUsage.addCommandLineOption("-p <filename>", "Play specified camera path animation file, previously saved with 'z' key.");
            arguments.ApplicationUsage.addCommandLineOption("--speed <factor>", "Speed factor for animation playing (1 == normal speed).");
            arguments.ApplicationUsage.addCommandLineOption("--device <device-name>", "add named device to the viewer");
            arguments.ApplicationUsage.addCommandLineOption("--stats", "print out load and compile timing stats");

            var viewer = new Viewer(arguments);

            var helpType = Osg.ApplicationUsage.Type.NO_HELP;
            if (helpType == arguments.readHelpType)
            {
                //arguments.ApplicationUsage.write(cout, helpType);
                //return;
            }

            // report any errors if they have occurred when parsing the program arguments.
            if (arguments.errors())
            {
                //arguments.writeErrorMessages(cout);
                return;
            }

            if (arguments.argc <= 1)
            {
                //arguments.ApplicationUsage.write(cout, Osg.ApplicationUsage::COMMAND_LINE_OPTION);
                return;
            }

            bool printStats = arguments.read("--stats");

            string url, username, password;
            while (arguments.read("--login", out url, out username, out password))
            {
                OsgDB.Registry.instance().getOrCreateAuthenticationMap().addAuthenticationDetails(
                    url,
                    new OsgDB.AuthenticationDetails(username, password)
                );
            }

            string device;
            while (arguments.read("--device", out device))
            {
                var dev = OsgDB.Static.readObjectFile(device) as OsgGA.Device;
                if (dev != null)
                {
                    viewer.addDevice(dev);
                }
            }

            // set up the camera manipulators.
            {
                var keyswitchManipulator = new OsgGA.KeySwitchMatrixManipulator();

                keyswitchManipulator.addMatrixManipulator('1', "Trackball", new OsgGA.TrackballManipulator());
                keyswitchManipulator.addMatrixManipulator('2', "Flight", new OsgGA.FlightManipulator());
                keyswitchManipulator.addMatrixManipulator('3', "Drive", new OsgGA.DriveManipulator());
                keyswitchManipulator.addMatrixManipulator('4', "Terrain", new OsgGA.TerrainManipulator());
                keyswitchManipulator.addMatrixManipulator('5', "Orbit", new OsgGA.OrbitManipulator());
                keyswitchManipulator.addMatrixManipulator('6', "FirstPerson", new OsgGA.FirstPersonManipulator());
                keyswitchManipulator.addMatrixManipulator('7', "Spherical", new OsgGA.SphericalManipulator());

                string pathfile;
                double animationSpeed = 1.0;
                while (arguments.read("--speed", out animationSpeed)) { }
                char keyForAnimationPath = '8';
                while (arguments.read("-p", out pathfile))
                {
                    var apm = new OsgGA.AnimationPathManipulator(pathfile);
                    if (apm != null && !apm.AnimationPath.empty)
                    {
                        apm.TimeScale = animationSpeed;

                        uint num = keyswitchManipulator.NumMatrixManipulators;
                        keyswitchManipulator.addMatrixManipulator(keyForAnimationPath, "Path", apm);
                        keyswitchManipulator.selectMatrixManipulator(num);
                        ++keyForAnimationPath;
                    }
                }

                viewer.CameraManipulator = keyswitchManipulator;
            }

            // add the state manipulator
            viewer.addEventHandler(new OsgGA.StateSetManipulator(viewer.Camera.getOrCreateStateSet()));

            // add the thread model handler
            viewer.addEventHandler(new OsgViewer.ThreadingHandler());

            // add the window size toggle handler
            viewer.addEventHandler(new OsgViewer.WindowSizeHandler());

            // add the stats handler
            viewer.addEventHandler(new OsgViewer.StatsHandler());

            // add the help handler
            viewer.addEventHandler(new OsgViewer.HelpHandler(arguments.ApplicationUsage));

            // add the record camera path handler
            viewer.addEventHandler(new OsgViewer.RecordCameraPathHandler());

            // add the LOD Scale handler
            viewer.addEventHandler(new OsgViewer.LODScaleHandler());

            // add the screen capture handler
            viewer.addEventHandler(new OsgViewer.ScreenCaptureHandler());

            var elapsedTime = new Osg.ElapsedTime();

            // load the data
            var loadedModel = OsgDB.Static.readNodeFiles(arguments);
            if (loadedModel == null)
            {
                //cout << arguments.ApplicationName << ": No data loaded" << endl;
                return;
            }

            if (printStats)
            {
                double loadTime = elapsedTime.elapsedTime_m;
                //cout << "Load time " << loadTime << "ms" << endl;

                viewer.Stats.collectStats("compile", true);
            }


            // any option left unread are converted into errors to write out later.
            arguments.reportRemainingOptionsAsUnrecognized();

            // report any errors if they have occurred when parsing the program arguments.
            if (arguments.errors())
            {
                //arguments.writeErrorMessages(cout);
                return;
            }


            // optimize the scene graph, remove redundant nodes and state etc.
            var optimizer = new OsgUtil.Optimizer();
            optimizer.optimize(loadedModel);

            viewer.SceneData = loadedModel;

            viewer.realize();

            viewer.run();
        }
    }
}
